﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using ActorID = System.Guid;
using System.Threading;

namespace NIF.MobileActors.Core
{
    [Serializable]
    public abstract class ActorBase<T> : IActor
    {
        protected abstract void Receive(Action<T> message);

        protected ActorBase()
        {
            ID = ActorID.NewAgentID();
            this.m_context = new ActorContext(this);
        }

        public virtual void ResetForExecute()
        {
            msgQueue = new Queue<Action<T>>();
            m_exited = false;
        }

        public void ResetForExecute(IActor actNewState)
        {
            ResumeState(actNewState);
            m_actived = true;
        }

        protected abstract void ResumeState(IActor actNewState);

        public virtual void Reset()
        {
            ID = ActorID.NewAgentID();
            this.m_context = new ActorContext(this);
            ResetForExecute();
        }

        public void Activate()
        {
            this.m_actived = true;
            int status = Interlocked.Exchange(
                ref Context.m_status,
                ActorContext.WAITING);
            Dispatcher.Instance.ReadyToExecute(this);
        }

        public void Deactivate()
        {
            this.m_actived = false;
        }

        protected void Exit()
        {
            this.m_exited = true;
        }

        public ActorID ID { get; private set; }

        public abstract void OnDispatched();

        public void Execute()
        {
            Action<T> message;
            if (this.msgQueue.Count <= 0)
                return;
            lock (this.msgQueue)
            {
                if (this.MessageCount > 0)
                {
                    message = this.msgQueue.Dequeue();
                }
                else
                    return;
            }
            this.Receive(message);
        }

        internal void Post(Action<T> action)
        {
            if (this.m_exited) return;

            lock (this.msgQueue)
            {
                this.msgQueue.Enqueue(new Action<T>(action));
            }

            Dispatcher.Instance.ReadyToExecute(this);
        }
        
        public bool Existed
        {
            get { return this.m_exited; }
        }

        public bool IsActiving
        {
            get { return this.m_actived; }
        }

        public int MessageCount
        {
            get { return this.msgQueue.Count; }
        }

        public ActorContext Context
        {
            get { return this.m_context; }
        }

        private ActorContext m_context = null;

        [NonSerialized]
        private Queue<Action<T>> msgQueue = new Queue<Action<T>>();
        [NonSerialized]
        private bool m_exited = false;
        [NonSerialized]
        private bool m_actived = true;
    }
}
